function [cost, results] = FitnessFunc_CBonly(x, mpc, P_loss0, V_dev0)
%% 阶段1适应度：仅优化 CB 台数（SVG 禁止介入）
% 约束：Q 不倒送；PCC PF ∈ [pf_lo, pf_hi]（默认 0.89~0.91）；电压不越限
% 说明：
% - mpc.cb(:,2) 为“pu 步长”（相对 baseMVA）；每步无功 = cb(:,2) * baseMVA (Mvar)
% - 兼容任意数量的 CB 组（Ncb = size(mpc.cb,1)）
% - 决策向量 x 的“最后 Ncb 个分量”映射到各 CB 组台数（0~max_units，取整）

%% —— 参数（可从 mpc.settings 覆写）——
pf_lo_def      = 0.89;     % PF 下限（给 SVG 留余量）
pf_hi_def      = 0.91;     % PF 上限（避免过补）
P_deadband_def = 0.00;     % 近零功率死区（建议 0：双向都考核）
keep_ratio_def = 0.05;     % 本地不过补：至少保留 5% 感性无功

if isfield(mpc,'settings')
    S = mpc.settings;
else
    S = struct();
end
pf_lo      = getfield_or(S,'pf_lo_cb',      pf_lo_def);
pf_hi      = getfield_or(S,'pf_hi_cb',      pf_hi_def);
P_deadband = getfield_or(S,'P_deadband_cb', P_deadband_def);
cb_keep_ratio_of_Qd = getfield_or(S,'keep_ratio_cb', keep_ratio_def);

% 其他权重/限制（一般不改）
alpha1 = 0.7; alpha2 = 0.3;   % 网损/电压偏差权重
vmax_hi = 1.05; vmax_lo = 0.95;
enforce_no_Q_export = true;   % 禁止倒送无功
Q_export_tol        = 0.02;   % Mvar 容差

%% —— 解码 CB 台数（通用 Ncb）——
Ncb = size(mpc.cb, 1);
n   = numel(x);
if n < Ncb
    error('FitnessFunc_CBonly: 决策向量长度 %d 小于 CB 组数 %d。', n, Ncb);
end
cb_idx = (n - Ncb + 1) : n;   % x 的最后 Ncb 个分量
CB_steps = zeros(Ncb,1);
for i = 1:Ncb
    max_units = mpc.cb(i,3);
    xi = x(cb_idx(i));
    CB_steps(i) = min(max_units, max(0, round(xi * max_units)));
end

%% —— 投 CB：严格“整数台数 × 步长”，带本地不过补 —— 
Qstep_Mvar = mpc.cb(:,2) * mpc.baseMVA;   % 每组的每步 Mvar（cb(:,2) 为 pu）
m = mpc;
Q_cb_applied = zeros(Ncb,1);

for i = 1:Ncb
    bi    = mpc.cb(i,1);           % 该 CB 作用的负荷母线
    Qd0   = mpc.bus(bi, 4);        % 原始无功负荷 (Mvar)
    stepM = Qstep_Mvar(i);         % 每步容量 (Mvar)
    req_steps = CB_steps(i);       % 目标台数（整数）

    % 本地不过补：至少保留 keep_ratio 的感性无功
    Qd_floor  = cb_keep_ratio_of_Qd * max(Qd0, 0);
    Q_allow   = max(Qd0 - Qd_floor, 0);                % 最多可抵消量
    allow_steps   = floor(Q_allow/stepM + 1e-9);       % 最多可投入台数
    applied_steps = min(req_steps, allow_steps);       % 实际投入台数
    Qcb = applied_steps * stepM;

    Q_cb_applied(i) = Qcb;
    m.bus(bi, 4) = Qd0 - Qcb;                          % 更新该母线 Qd
end

%% —— 阶段1禁 SVG：固定 SVG 无功 = 0 —— 
if isfield(m,'svg') && isfield(m.svg,'gen_idx') && ~isempty(m.svg.gen_idx)
    gidx = m.svg.gen_idx;
    m.gen(gidx,3) = 0;   % Qg
    m.gen(gidx,4) = 0;   % Qmax
    m.gen(gidx,5) = 0;   % Qmin
end

%% —— 潮流 —— 
try
    r = runpf(m);
    if ~r.success, error('PF 不收敛'); end
catch
    cost    = 1e6;
    results = fail_result(CB_steps, Q_cb_applied);
    return;
end

%% —— 指标/约束 —— 
[P, Q, pf] = slackPQPF(r);     % PCC（slack）处 P/Q/PF
pf_abs = abs(pf);
V = r.bus(:, 8);

% 电压越限罚
v_violation = sum(max(0, V - vmax_hi)) + sum(max(0, vmax_lo - V));
penalty = 300 * v_violation;

% 禁止倒送无功（容性为负）
if enforce_no_Q_export && (Q < -Q_export_tol)
    penalty = penalty + 1000 * (-Q - Q_export_tol);
end

% PF 带宽（仅当 |P| > 死区才考核）
if abs(P) > P_deadband
    if pf_abs < pf_lo
        penalty = penalty + 800 * (pf_lo - pf_abs);   % 欠补
    elseif pf_abs > pf_hi
        penalty = penalty + 400 * (pf_abs - pf_hi);   % 过补（若 Q<0 已被上面惩罚）
    end
end

% 目标项：网损、电压偏差
loss = real(sum(get_losses(r)));
vdev = sum(abs(V - 1.0));

% 归一化，防 0/NaN
if ~isfinite(P_loss0) || P_loss0 <= 0, P_loss0 = max(loss, 1e-6); end
if ~isfinite(V_dev0)  || V_dev0  <= 0, V_dev0  = max(vdev, 1e-6); end
f_loss = loss / P_loss0;
f_vdev = vdev / V_dev0;

% 综合成本
cost = alpha1 * f_loss + alpha2 * f_vdev + penalty;

%% —— 输出 —— 
results = struct();
results.bus      = r.bus;
results.gen      = r.gen;
results.branch   = r.branch;
results.loss     = loss;
results.v_dev    = vdev;
results.P_pcc    = P;
results.Q_pcc    = Q;
results.pf       = pf_abs;
results.CB_steps = CB_steps;         % 长度 = Ncb
results.Q_cb     = Q_cb_applied;     % 长度 = Ncb（各母线实际投入的 Mvar）
results.Q_svc    = 0;                % 阶段1 不使用 SVG
results.cost     = cost;
end

%% ===== 工具函数 =====
function val = getfield_or(S, fname, def)
if isfield(S, fname), val = S.(fname); else, val = def; end
end

function R = fail_result(CB_steps, Q_cb_applied)
R = struct('loss',NaN,'v_dev',NaN,'penalty',1e6,'cost',1e6, ...
           'P_pcc',NaN,'Q_pcc',NaN,'pf',NaN, ...
           'CB_steps',CB_steps,'Q_cb',Q_cb_applied,'Q_svc',0);
end

function [P_slack, Q_slack, pf_val] = slackPQPF(res)
sl = (res.gen(:,1) == 1);       % 平衡母线 = 1
P_slack = sum(res.gen(sl, 2));
Q_slack = sum(res.gen(sl, 3));
S = hypot(P_slack, Q_slack);
if S < 1e-9, pf_val = 1.0; else, pf_val = P_slack / S; end
end
